package com.sprouting.ops.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import com.sprouting.ops.interfaces.CacheKey;
import com.sprouting.ops.interfaces.ConstantPower;
import com.sprouting.ops.model.OpsUser;
import com.sprouting.ops.model.ResultJson;
import com.sprouting.ops.service.OpsUserService;
import com.sprouting.ops.utils.CheckTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 管理员控制器
 *
 * DependsOn 注解作用是，先加载 autoDatabaseCreate 这个bean，默认的情况下，bean的加载顺序就是看到的这种顺序加载，但这个时候还没有对应的
 * 数据库，这个时候就需要先优先加载自动创建数据库的bean，故而用这个注解
 *
 * @author ：LX（长沙麓谷）
 * @date 创建时间： 2023/3/18 17:27
 */
@DependsOn("autoDatabaseCreate")
@RestController
@RequestMapping("admin")
public class AdminController {

    @Autowired
    private OpsUserService opsUserService;

    /**
     * 超管用户，根据授权的token 获取到登录用户
     * @param tokenV 要查询的token
     * @return
     */
    @SaCheckPermission(ConstantPower.Admin.GET_USER_BY_TOKEN)
    @PostMapping("getUserByToken")
    public ResultJson<OpsUser> getUserByToken(String tokenV){
        if (CheckTool.checkNull(tokenV)){
            return ResultJson.errorCheck();
        }

        String id = (String) StpUtil.getLoginIdByToken(tokenV);
        if (CheckTool.checkNull(id)){
            return ResultJson.error("无效的token");
        }

        /*
        //获取上面这个账号的session,一般基于账号session
        SaSession saSession = StpUtil.getSessionByLoginId(id, false);

        // 获取指定token的专属Session
        SaSession tokenSession = StpUtil.getTokenSessionByToken(token);

        // 返回当前token的登录设备
        String loginDevice = StpUtil.getLoginDevice();

        // 获取指定loginId指定设备端的tokenValue
        StpUtil.getTokenValueByLoginId(10001, ConstantAuthority.LoginType.PC);

        */

        OpsUser user = opsUserService.getById(id);
        return ResultJson.success(user);
    }

    /**
     * 根据ID查询该用户现在的token信息
     * @param id 用户ID
     * @param loginType 登录方式，可以为空 {@link com.sprouting.ops.interfaces.ConstantBase.LoginType}
     * @return
     */
    @PostMapping("/getUserByIdToken")
    @SaCheckRole(ConstantPower.Base.BASE_ROLE_CODE)
    public ResultJson<String> getUserByIdToken(String id, String loginType){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorCheck();
        }
        String tokenvalue;
        if (CheckTool.checkNull(loginType)){
            tokenvalue = StpUtil.getTokenValueByLoginId(id);
        } else {
            // 获取指定loginId指定设备端的tokenValue
            tokenvalue = StpUtil.getTokenValueByLoginId(id, loginType);
        }

        return ResultJson.success(tokenvalue);
    }


    /*
    -------------------------------------------------------------------------------------------------------------------------------------
        踢人下线不会清除Token信息，而是将其打上特定标记，再次访问会提示：Token已被踢下线。
    -------------------------------------------------------------------------------------------------------------------------------------
     */

    /**
     * 根据用户ID踢人下线
     * @param userId 用户ID {@link OpsUser#getId()}
     * @return
     */
    @SaCheckRole(ConstantPower.Base.BASE_ROLE_CODE)
    @PostMapping("kickPeople")
    public ResultJson kickPeople(String userId){
        if (CheckTool.checkNull(userId)){
            return ResultJson.errorCheck();
        }
        StpUtil.kickout(userId);
        return ResultJson.success();
    }

    /**
     * 将指定账号指定端踢下线
     * @param userId 用户ID
     * @param loginType 指定登录端 {@link com.sprouting.ops.interfaces.ConstantBase.LoginType}
     * @return
     */
    @PostMapping("kickPeopleType")
    public ResultJson kickPeopleType(String userId, String loginType){
        if (CheckTool.checkNull(userId, loginType)){
            return ResultJson.errorCheck();
        }

        StpUtil.kickout(userId, loginType);
        return ResultJson.success();
    }

    /**
     * 将指定 Token 踢下线
     * @param token token值
     * @return
     */
    @PostMapping("kickPeopleToken")
    public ResultJson kickPeopleToken(String token){
        if (CheckTool.checkNull(token)){
            return ResultJson.errorCheck();
        }

        StpUtil.kickoutByTokenValue(token);
        return ResultJson.success();
    }


    /*
    -------------------------------------------------------------------------------------------------------------------------------------
        强制注销等价于对方主动调用了注销方法，再次访问会提示：Token无效。
    -------------------------------------------------------------------------------------------------------------------------------------
     */


    /**
     * 强制指定账号注销下线
     * @param userId 用户ID {@link OpsUser#getId()}
     * @return
     */
    @PostMapping("logout")
    @SaCheckRole(ConstantPower.Base.BASE_ROLE_CODE)
    public ResultJson logout(String userId){
        if (CheckTool.checkNull(userId)){
            return ResultJson.errorCheck();
        }
        StpUtil.logout(userId);
        return ResultJson.success();
    }

    /**
     * 强制指定账号指定端注销下线
     * @param userId 用户ID
     * @param loginType 指定登录端 {@link com.sprouting.ops.interfaces.ConstantBase.LoginType}
     * @return
     */
    @PostMapping("logoutType")
    public ResultJson logoutType(String userId, String loginType){
        if (CheckTool.checkNull(userId, loginType)){
            return ResultJson.errorCheck();
        }

        StpUtil.logout(userId, loginType);
        return ResultJson.success();
    }

    /**
     * 强制指定 Token 注销下线
     * @param token token值
     * @return
     */
    @SaCheckRole(ConstantPower.Base.BASE_ROLE_CODE)
    @PostMapping("logoutByTokenValue")
    public ResultJson logoutByTokenValue(String token){
        if (CheckTool.checkNull(token)){
            return ResultJson.errorCheck();
        }

        StpUtil.logoutByTokenValue(token);
        return ResultJson.success();
    }





    /*
    -------------------------------------------------------------------------------------------------------------------------------------
        sa-token 的 session
    -------------------------------------------------------------------------------------------------------------------------------------
     */

    /**
     * 获取session中的user信息
     * @param userId 用户ID
     * @return
     */
    @PostMapping("getSessionUser")
    public ResultJson getSessionUser(String userId){
        if (CheckTool.checkNull(userId)){
            return ResultJson.errorCheck();
        }

        SaSession saSession = StpUtil.getSessionByLoginId(userId);
        OpsUser user = saSession.getModel(CacheKey.SaTokenSession.SESSION_KEY_USER, OpsUser.class);
        return ResultJson.success(user);
    }


    /*
    -------------------------------------------------------------------------------------------------------------------------------------
        其他
    -------------------------------------------------------------------------------------------------------------------------------------
     */


    /**
     * 根据token获取对应的账号ID
     * @param token 要获取的token数据
     * @return 对应账号ID，无数据，则返回空
     */
    @PostMapping("token")
    public ResultJson getLoginIdByToken(String token){
        if (CheckTool.checkNull(token)){
            return ResultJson.errorCheck();
        }
        // 获取指定token对应的账号id，如果未登录，则返回 null
        return ResultJson.success(StpUtil.getLoginIdByToken(token));
    }


    /**
     * 获取指定loginId指定设备类型端的tokenValue
     * @param userId 用户ID
     * @param loginType 指定登录端 {@link com.sprouting.ops.interfaces.ConstantBase.LoginType}
     * @return
     */
    @PostMapping("getTokenValueByLoginId")
    public ResultJson getTokenValueByLoginId(String userId, String loginType){
        if (CheckTool.checkNull(userId, loginType)){
            return ResultJson.errorCheck();
        }

        return ResultJson.success(StpUtil.getTokenValueByLoginId(userId, loginType));
    }



     /*
    -------------------------------------------------------------------------------------------------------------------------------------
        封禁
    -------------------------------------------------------------------------------------------------------------------------------------
     */

    /**
     * 封禁，在redis层面的封禁，sa-token实现。注意，下次登录的时候需要验证是否封禁，StpUtil.checkDisable(10001);  否则不会走该逻辑。v1.31.0 之后将 校验封禁 和 登录 两个动作分离成两个方法，不再自动校验
     * @param userId 要封禁的账号id
     * @param time 封禁时间，单位：秒，此为 86400秒 = 1天（此值为 -1 时，代表永久封禁）
     * @param kickPeople 是否先踢人下线，true 先踢人，false 不踢人 对于正在登录的账号，将其封禁并不会使它立即掉线，如果我们需要它即刻下线，可采用先踢再封禁的策略.
     * @return
     */
    @PostMapping("disable")
    public ResultJson disable(String userId, Integer time, Boolean kickPeople){
        if (CheckTool.checkNull(userId)){
            return ResultJson.errorCheck();
        }
        if (time == null){
            return ResultJson.errorCheck();
        }
        if (time < 1){
            return ResultJson.error("不合法的封禁时间");
        }
        if (kickPeople == null){
            kickPeople = true;
        }
        if (kickPeople){
            StpUtil.kickout(userId);
        }
        StpUtil.disable(userId, time);
        return ResultJson.success();
    }

    /**
     * 解除封禁
     * @param userId 要封禁的账号id
     * @return
     */
    @PostMapping("untieDisable")
    public ResultJson untieDisable(String userId){
        if (CheckTool.checkNull(userId)){
            return ResultJson.errorCheck();
        }
        StpUtil.untieDisable(userId);
        return ResultJson.success();
    }


    /*
    -------------------------------------------------------------------------------------------------------------------------------------
        统计
    -------------------------------------------------------------------------------------------------------------------------------------
     */

    /**
     * 获取当前登录的人数
     * @return
     */
    @GetMapping("getAllUserNum")
    public ResultJson getAllUserNum(){
        // 参数一，查询关键字，只有包括这个字符串的 token 值才会被查询出来。
        // 参数二，数据开始处索引。
        // 参数三，要获取的数据条数 （值为-1代表一直获取到末尾）。
        // 参数四，排序方式（true=正序：先登录的在前，false=反序：后登录的在前）
        int size = StpUtil.searchSessionId("", 0, -1, false).size();
        // for (int i = page * size, len = Math.min(size + i, logIds.size()); i < len; i++) {
        //     session = StpUtil.getSessionBySessionId(logIds.get(i));
        //     list.add(session.getModel("user_info", LoginUserRespVO.class));
        // }
        return ResultJson.success(size);
    }

}
